(in-package :cl-fast-ecs)


(defmacro defhook (name &key (documentation ""))
  (let ((var-name (format-symbol :cl-fast-ecs "*~a-HOOK*" name)))
    `(progn
       (declaim (type simple-vector ,var-name))
       (define-global-var ,var-name (make-array 0) ,documentation))))

(defmacro hook-up (hook fn)
  "Hooks up the function `FN` on the `HOOK`.

Complexity: *O(m)*, where *m* is the number of functions on a given hook.

See also `UNHOOK`."
  (let ((var-name (intern (string hook) :cl-fast-ecs)))
    `(locally
         #-clisp (proclaim '(#+sbcl sb-ext:global
                             #+lispworks hcl:special-global
                             #-(or sbcl lispworks) special
                             ,var-name))
         (adjust-simple-arrayf ,var-name (1+ (length ,var-name))
                               :element-type 'function
                               :initial-element ,fn))))

(defmacro unhook (hook fn)
  "Unhooks the function `FN` from the `HOOK`.

If the function wasn't registered with `HOOK-UP` on a given hook, this
macro has no effect.

Complexity: *O(m)*, where *m* is the number of functions on a given hook."
  (let ((var-name (intern (string hook) :cl-fast-ecs)))
    `(locally
         #-clisp (proclaim '(#+sbcl sb-ext:global
                             #+lispworks hcl:special-global
                             #-(or sbcl lispworks) special
                             ,var-name))
         (setf ,var-name (remove ,fn ,var-name)))))

(declaim (ftype (function (simple-vector &rest t)) run-hook))
(defun run-hook (hook &rest args)
  (loop :for fn :of-type function :across hook :do (apply fn args)))
